package com.clss.commandhelper.tools.janus.plugin;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.clss.commandhelper.tools.Lg;
import com.clss.commandhelper.tools.SpHelper;
import com.clss.commandhelper.tools.janus.entity.MediaSessionBean;
import com.clss.commandhelper.tools.janus.entity.PublisherInfo;
import com.clss.commandhelper.tools.janus.janusclientapi.IJanusGatewayCallbacks;
import com.clss.commandhelper.tools.janus.janusclientapi.IJanusPluginCallbacks;
import com.clss.commandhelper.tools.janus.janusclientapi.IPluginHandleWebRTCCallbacks;
import com.clss.commandhelper.tools.janus.janusclientapi.JanusMediaConstraints;
import com.clss.commandhelper.tools.janus.janusclientapi.JanusMediaConstraints.Camera;
import com.clss.commandhelper.tools.janus.janusclientapi.JanusPluginHandle;
import com.clss.commandhelper.tools.janus.janusclientapi.JanusServer;
import com.clss.commandhelper.tools.janus.janusclientapi.JanusSupportedPluginPackages;
import com.clss.commandhelper.tools.janus.janusclientapi.PluginHandleSendMessageCallbacks;
import com.clss.commandhelper.tools.janus.janusclientapi.PluginHandleWebRTCCallbacks;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.RendererCommon;
import org.webrtc.VideoRenderer;
import org.webrtc.VideoRendererGui;

import java.util.ArrayList;
import java.util.List;

/**
 * @author ben.trent
 * @date 7/24/2015
 */

public class VideoRoomPlugin {
    private static final String TAG = "VideoRoomPlugin";

    private static final int PUBLISH_LEVEL_NONE = 0;

    private JanusPluginHandle mHandle = null;
    private VideoRenderer.Callbacks mPreviewRenderCallback, mMainRenderCallback;
    private VideoRenderer mPieviewRederer, mMainRenderer;
    private JanusServer mJanusServer;
    private List<PublisherInfo> mPublisherList = new ArrayList<>();
    private MediaSessionBean mMediaSessionBean;
    private Camera mCurrentCamera = Camera.back;
    private String mShowingDesplay;
    private MediaStream mMediaStream;

    public VideoRoomPlugin(MediaSessionBean mediaSessionBean) {
        this.mMainRenderCallback = VideoRendererGui.create(0, 0, 100, 100,
                RendererCommon.ScalingType.SCALE_ASPECT_FILL, false);
        this.mPreviewRenderCallback = VideoRendererGui.create(0, 0, 25, 25,
                RendererCommon.ScalingType.SCALE_ASPECT_FILL, false);

        this.mMediaSessionBean = mediaSessionBean;
        this.mJanusServer = new JanusServer(new JanusGlobalCallbacks());
    }

    public boolean initializeMediaContext(Context context){
        return mJanusServer.initializeMediaContext(context, true, true, true);
    }

    public void start() {
        mJanusServer.Connect();
    }

    public void destroy(){
        mPublisherList.clear();
        mJanusServer.Destroy();
    }

    public void switchCamera(){
        mHandle.switchCamera();
    }

    /**
     * 发送本次视频通话网络配置
     */
    public class JanusGlobalCallbacks implements IJanusGatewayCallbacks {
        @Override
        public void onSuccess() {
            mJanusServer.Attach(new JanusPluginCallbacks());
        }

        @Override
        public void onDestroy() {
        }

        @Override
        public String getServerUri() {
            return mMediaSessionBean.getServer();
        }

        @Override
        public List<PeerConnection.IceServer> getIceServers() {
            ArrayList<PeerConnection.IceServer> iceServers = new ArrayList<>();
            iceServers.add(new PeerConnection.IceServer(mMediaSessionBean.getTurnAddress(),
                    mMediaSessionBean.getTurnUser(), mMediaSessionBean.getTurnPassword()));
//            iceServers.add(new PeerConnection.IceServer("stun:222.223.124.158:3478",
//                    "ninelives", "kEvinSpACY"));
            return iceServers;
        }

        @Override
        public Boolean getIpv6Support() {
            return Boolean.FALSE;
        }

        @Override
        public Integer getMaxPollEvents() {
            return 0;
        }

        @Override
        public void onCallbackError(String error) {

        }
    }

    /**
     *  WebSocket连接成功后运行
     */
    public class JanusPluginCallbacks implements IJanusPluginCallbacks {
        @Override
        public void success(JanusPluginHandle pluginHandle) {
            VideoRoomPlugin.this.mHandle = pluginHandle;

            // 发送获取到的房间参数
            JSONObject msg = new JSONObject();
            JSONObject obj = new JSONObject();
            try {
                obj.put("request", "join");
                obj.put("room", mMediaSessionBean.getRoom());
                obj.put("ptype", "publisher");
                obj.put("display", getMineDisplay());
                obj.put("secret", mMediaSessionBean.getSecret());
                obj.put("pin", mMediaSessionBean.getPin());

                msg.put("message", obj);
                mHandle.sendMessage(new PluginHandleSendMessageCallbacks(msg));
            } catch (Exception ex) {

            }
        }

        @Override
        public void onMessage(JSONObject msg, final JSONObject jsepLocal) {
            Log.i(TAG, "---JanusPluginCallbacks===msg===" + msg.toString());
            if (msg.has("videoroom")) {
                try {
                    String event = msg.getString("videoroom");
                    switch (event) {
                        case "joined":
                            Log.i(TAG, "---publish===receive===joined===");
                            sendLocalJanusConfig(mCurrentCamera);
                            if (!msg.has("publishers")) {
                                break;
                            }
                            //TODO 在自己进入房间前，已经存在的视频发布者，依次保存id并对其监听
                            receivePublisherArray(msg);
                            break;
                        case "event":
                            if (msg.has("publishers")) {
                                Log.i(TAG, "---publish===receive===event===publishers===");
                                //TODO 在自己加入房间后进入房间的视频发布者，依次保存id并对其监听
                                receivePublisherArray(msg);
                            } else if (msg.has("leaving")) {
                                Log.i(TAG, "---publish===receive===event===leaving===");

                            } else if (msg.has("unpublished")) {
                                Log.i(TAG, "---publish===receive===event===unpublished===");
                                long removeId = msg.getLong("unpublished");
                                //TODO 删除本地对这个id的监听
                                removePublisher(removeId);
                            } else if (msg.has("configured")) {
                                Log.i(TAG, "---publish===receive===event===configured===");
                            }
                            break;
                        case "destroyed":
                            Log.i(TAG, "---publish===receive===destroyed===");
                            break;
                        case "participants":
                            Lg.i(TAG, "---publish===receive===participants===");
                            refreshPublisherResult(msg);
                            break;
                        default:
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    Log.i(TAG, "---JSONException===" + e.getMessage());
                }
            }else {
                Log.i(TAG, "---msg.none.videoroom===");
            }

            //每次收到message都要更新一下jsep
            if(jsepLocal != null){
                mHandle.handleRemoteJsep(new PluginHandleWebRTCCallbacks(null, jsepLocal, false));
            }
        }

        @Override
        public void onLocalStream(MediaStream stream) {
            Lg.i(TAG, "---JanusPluginCallbacks===onLocalStream===" + stream.videoTracks.size());
            mPieviewRederer = new VideoRenderer(mPreviewRenderCallback);
            stream.videoTracks.get(0).addRenderer(mPieviewRederer);
        }

        @Override
        public void onRemoteStream(MediaStream stream) {
            Lg.i(TAG, "---JanusPluginCallbacks===onRemoteStream===" + stream.videoTracks.size());
        }

        @Override
        public void onDataOpen(Object data) {

        }

        @Override
        public void onData(Object data) {

        }

        @Override
        public void onCleanup() {
            Lg.i(TAG, "---JanusPluginCallbacks===onCleanup===");
        }

        @Override
        public JanusSupportedPluginPackages getPlugin() {
            return JanusSupportedPluginPackages.JANUS_VIDEO_ROOM;
        }

        @Override
        public void onCallbackError(String error) {
            Lg.i(TAG, "---JanusPluginCallbacks===onCallbackError===" + error);
        }

        @Override
        public void onDetached() {
            Lg.i(TAG, "---JanusPluginCallbacks===onDetached===");
        }
    }

    /**
     * 发送本地janus配置
     */
    private void sendLocalJanusConfig(JanusMediaConstraints.Camera camera){
        mHandle.createOffer(new IPluginHandleWebRTCCallbacks() {
            @Override
            public JSONObject getJsep() {
                return null;
            }

            @Override
            public void onCallbackError(String error) {

            }

            @Override
            public Boolean getTrickle() {
                return true;
            }

            @Override
            public JanusMediaConstraints getMedia() {
                JanusMediaConstraints localMediaCon = new JanusMediaConstraints();
                localMediaCon.setRecvAudio(false);
                localMediaCon.setRecvVideo(false);
                localMediaCon.setSendAudio(true);
                localMediaCon.setCamera(camera);
                return localMediaCon;
            }

            @Override
            public void onSuccess(JSONObject obj) {
                Log.i("JANUSCLIENT", "OnSuccess for CreateOffer called");
                try {
                    JSONObject body = new JSONObject();
                    JSONObject msg = new JSONObject();

                    body.put("audio", true);
                    body.put("video", true);
                    body.put("request", "configure");

                    msg.put("message", body);
                    msg.put("jsep", obj);
                    mHandle.sendMessage(new PluginHandleSendMessageCallbacks(msg));
                } catch (Exception ex) {

                }
            }
        });
    }

    /**
     * 处理收到的publisher列表
     * @param msg 包含列表的jsonobj
     */
    private void receivePublisherArray(JSONObject msg){
        try {
            JSONArray publisherArray = msg.getJSONArray("publishers");
            Lg.i(TAG, "---receivePublisherArray===" + publisherArray.toString());
            for (int p = 0; p < publisherArray.length(); p++) {
                JSONObject publisher = null;
                publisher = publisherArray.getJSONObject(p);
                long publisherId = publisher.getLong("id");
                String publisherDisplay = publisher.getString("display");
                saveAndListenerPublisher(publisherId, publisherDisplay);
            }
        } catch (JSONException e) {
            Lg.e(TAG, "---receivePublisherArray===" + msg.toString());
            e.printStackTrace();
        }
    }

    /**
     * 处理接收到的每一个发布者
     * 如果需要呈现该发布者的视频和音频，则发起监听绑定该发布者
     * 如果该发布者的level为0，则不监听他
     */
    private void saveAndListenerPublisher(long publisherId, String publisherDisplay){
        Lg.i(TAG, "---saveAndListenerPublisher===" + publisherDisplay);
        mPublisherList.add(new PublisherInfo(publisherId, publisherDisplay));

        if (getDisplayLevel(publisherDisplay) == PUBLISH_LEVEL_NONE){
            return;
        }

        long lastId = getLastShowPublisherId();
        if (JanusPluginHandle.sMediaStreamMap.containsKey(lastId) &&
                JanusPluginHandle.sMediaStreamMap.get(lastId) != null){
            showPublisherImage(JanusPluginHandle.sMediaStreamMap.get(lastId), getDisplayFromId(lastId));
            return;
        }
        //TODO 请求监听该发布者，以publisherId为依据
        mJanusServer.Attach(new ListenerAttachCallbacks(lastId, getDisplayFromId(lastId)));
    }

    /**
     * 发布者退出房间时取消对其的监听
     */
    private void removePublisher(long removeId){
        //重新获取房间内成员列表
        refreshPublisherList();
        //删除对应mediaStream
        JanusPluginHandle.sMediaStreamMap.remove(removeId);
    }

    /**
     * 当前视频房间的参与者列表
     * @param msg
     */
    private void refreshPublisherResult(JSONObject msg){
        Lg.i(TAG, "---refreshPublisherResult===" + msg.toString());
        try {
            JSONArray publisherArray = msg.getJSONArray("participants");
            if (publisherArray.length() == 0){
                return;
            }
            mPublisherList.clear();
            for (int p = 0; p < publisherArray.length(); p++) {
                JSONObject publisher = publisherArray.getJSONObject(p);
                if (!publisher.getBoolean("publisher")){
                    continue;
                }
                String publisherDisplay = publisher.getString("display");
                if (getMineDisplay().equals(publisherDisplay)){
                    continue;
                }
                long publisherId = publisher.getLong("id");
                Lg.i(TAG, "---refreshPublisherResult===mPublisherList.size===" + publisherDisplay);
                saveAndListenerPublisher(publisherId, publisherDisplay);
            }
        } catch (JSONException e) {
            Lg.e(TAG, "---receivePublisherArray===" + msg.toString());
            e.printStackTrace();
        }
    }

    /**
     * 开启对某一个publisher的监听
     */
    private class ListenerAttachCallbacks implements IJanusPluginCallbacks{
        private long publisherId;
        private String publisherDisplay;
        private JanusPluginHandle listenerHandle;

        public ListenerAttachCallbacks(long publisherId, String publisherDisplay) {
            this.publisherId = publisherId;
            this.publisherDisplay = publisherDisplay;
        }

        @Override
        public void onCallbackError(String error) {
            Lg.i(TAG, "---ListenerAttachCallbacks===onCallbackError===" + error);
        }

        @Override
        public void success(JanusPluginHandle handle) {
            listenerHandle = handle;
            try {
                JSONObject body = new JSONObject();
                JSONObject msg = new JSONObject();

                body.put("request", "join");
                body.put("room", mMediaSessionBean.getRoom());
                body.put("ptype", "listener");
                body.put("feed", publisherId);
                body.put("secret", mMediaSessionBean.getSecret());
                body.put("pin", mMediaSessionBean.getPin());

                msg.put("message", body);

                handle.sendMessage(new PluginHandleSendMessageCallbacks(msg));
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onMessage(JSONObject msg, JSONObject jsep) {
            try {
                if (null == jsep){
                    return;
                }
                String event = msg.getString("videoroom");
                if (event.equals("attached")){
                    listenerHandle.createAnswer(publisherId, new IPluginHandleWebRTCCallbacks() {
                        @Override
                        public void onSuccess(JSONObject obj) {
                            try {
                                JSONObject attachedAnswerMsg = new JSONObject();
                                JSONObject body = new JSONObject();

                                body.put("request", "start");
                                body.put("room", mMediaSessionBean.getRoom());
                                attachedAnswerMsg.put("message", body);
                                attachedAnswerMsg.put("jsep", obj);
                                listenerHandle.sendMessage(new PluginHandleSendMessageCallbacks(attachedAnswerMsg));
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public JSONObject getJsep() {
                            return jsep;
                        }

                        @Override
                        public JanusMediaConstraints getMedia() {
                            JanusMediaConstraints cons = new JanusMediaConstraints();
                            cons.setVideo(null);
                            cons.setRecvAudio(true);
                            cons.setRecvVideo(true);
                            cons.setSendAudio(false);
                            return cons;
                        }

                        @Override
                        public Boolean getTrickle() {
                            return true;
                        }

                        @Override
                        public void onCallbackError(String error) {

                        }
                    });
                }

            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onLocalStream(MediaStream stream) {
            Lg.i(TAG, "---ListenerAttachCallbacks===onLocalStream===" + stream.videoTracks.size());
        }

        @Override
        public void onRemoteStream(MediaStream stream) {
            if (stream.videoTracks.size() == 0){
                return;
            }
            if (!TextUtils.isEmpty(mShowingDesplay) &&
                    !JanusPluginHandle.sMediaStreamMap.containsKey(getIdFromDisplay(mShowingDesplay))){
                mShowingDesplay = null;
            }
            if (!TextUtils.isEmpty(mShowingDesplay) && getDisplayLevel(publisherDisplay) <= getDisplayLevel(mShowingDesplay)){
                return;
            }
            showPublisherImage(stream, publisherDisplay);
        }

        @Override
        public void onDataOpen(Object data) {

        }

        @Override
        public void onData(Object data) {

        }

        @Override
        public void onCleanup() {
            Lg.i(TAG, "---ListenerAttachCallbacks===onCleanup===");
        }

        @Override
        public void onDetached() {
            Lg.i(TAG, "---ListenerAttachCallbacks===onDetached===");
        }

        @Override
        public JanusSupportedPluginPackages getPlugin() {
            return JanusSupportedPluginPackages.JANUS_VIDEO_ROOM;
        }
    }

    /**
     * 在主窗口显示视频流
     * @param stream 视频流
     * @param publisherDisplay 该视频流对应的display
     */
    private void showPublisherImage(MediaStream stream, String publisherDisplay){
        Lg.i(TAG, "---showPublisherImage===" + stream.label());
        if (mMediaStream != null){
            mMediaStream.videoTracks.getFirst().setEnabled(false);
            mMediaStream.videoTracks.getFirst().removeRenderer(mMainRenderer);
        }
        mMainRenderer = new VideoRenderer(mMainRenderCallback);
        stream.videoTracks.getFirst().setEnabled(true);
        stream.videoTracks.getFirst().addRenderer(mMainRenderer);
        mShowingDesplay = publisherDisplay;
        mMediaStream = stream;
    }

    private void refreshPublisherList(){
        try {
            JSONObject body = new JSONObject();
            JSONObject msg = new JSONObject();

            body.put("request", "listparticipants");
            body.put("room", mMediaSessionBean.getRoom());

            msg.put("message", body);

            mHandle.sendMessage(new PluginHandleSendMessageCallbacks(msg));
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    private int getDisplayLevel(String display){
        if (TextUtils.isEmpty(display)){
            return 0;
        }
        String[] displayArray = display.split(":");
        return displayArray.length < 2 ? 0 : Integer.parseInt(displayArray[1]);
    }

    private String getDisplayId(String display){
        if (TextUtils.isEmpty(display)){
            return "";
        }
        String[] displayArray = display.split(":");
        return displayArray[0];
    }

    private long getIdFromDisplay(String display){
        if(TextUtils.isEmpty(display)){
            return 0;
        }
        for (PublisherInfo info : mPublisherList){
            if (display.equals(info.getDisplay())){
                return info.getId();
            }
        }
        return 0;
    }

    private String getDisplayFromId(long id){
        for (PublisherInfo info : mPublisherList){
            if (info.getId() == id){
                return info.getDisplay();
            }
        }
        return "";
    }

    private long getLastShowPublisherId(){
        if (mPublisherList.size() == 0){
            return 0;
        }
        int level = 0;
        long id = 0;
        for (PublisherInfo info : mPublisherList){
            if (getDisplayLevel(info.getDisplay()) < level){
                continue;
            }
            level = getDisplayLevel(info.getDisplay());
            id = info.getId();
        }
        return id;
    }

    private String getMineDisplay(){
        return SpHelper.getInstance().getUserId()+ ":" + mMediaSessionBean.getPublicLevel();
    }
}
